πŸ•ΈοΈ Ada Research Browser

MALWARE_DASHBOARD_INTEGRATION_PLAN.md
← Back

Malware Scanner Dashboard Integration Plan

Project: Integrate Alfred malware scanning reports into Security Dashboard Target Dashboard: https://8qdj5it341kfv92u.brandonquig.com/security-dashboard/ Status: Planning Phase Created: 2026-03-06


Executive Summary

Integrate malware scanning system (ClamAV, maldet, rkhunter, chkrootkit, Lynis) into the existing Security Dashboard to provide real-time visibility of system security posture, automated scan results, and threat detection.

Goals: 1. Display malware scan results in Security Dashboard 2. Incorporate scan metrics into overall security posture score 3. Provide historical trending of scan results 4. Alert on detected threats 5. Enable drill-down into scan logs


Current State Analysis

Existing Security Dashboard Architecture

Location: /var/www/html/alfred/dashboard/security-dashboard/

Structure:

security-dashboard/
β”œβ”€β”€ index.php                    # Main dashboard UI
β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ posture.php             # Overall security posture score
β”‚   β”œβ”€β”€ compliance.php          # Compliance controls
β”‚   β”œβ”€β”€ incidents.php           # Security incidents
β”‚   β”œβ”€β”€ redteam.php             # Red team attack results
β”‚   β”œβ”€β”€ alerts.php              # Security alerts
β”‚   └── lib/
β”‚       └── db.php              # Database connection
β”œβ”€β”€ css/
β”‚   └── security.css            # Dashboard styling
└── js/
    └── (JavaScript modules)

Database: PostgreSQL blueteam schema - posture_scores - Historical security scores - compliance_controls - Compliance tracking - security_incidents - Incident management - (Red team reports stored as JSON files)

Posture Score Calculation:

Overall Score =
  Compliance (35%) +
  Red Team (30%) +
  Incident (20%) +
  Monitoring (15%)

Malware Scanning System

Location: /opt/claude-workspace/shared-resources/scripts/

Tools Installed: - ClamAV - Daily antivirus scans (2 AM) - maldet - Daily web malware scans (3 AM) - rkhunter - Weekly rootkit scans (Sunday 4 AM) - chkrootkit - Weekly rootkit verification (Sunday 4:30 AM) - Lynis - On-demand security audits

Log Files: /var/log/malware-scans/ - clamav-YYYYMMDD.log - Daily - maldet-YYYYMMDD.log - Daily - rkhunter-YYYYMMDD.log - Weekly - chkrootkit-YYYYMMDD.log - Weekly

Current Gap: Scan results not visible in dashboard, no database integration


Proposed Architecture

1. Database Schema Extension

New Table: blueteam.malware_scans

CREATE TABLE blueteam.malware_scans (
    scan_id SERIAL PRIMARY KEY,
    scan_type VARCHAR(20) NOT NULL,  -- 'clamav', 'maldet', 'rkhunter', 'chkrootkit', 'lynis'
    scan_date TIMESTAMP NOT NULL DEFAULT NOW(),
    status VARCHAR(20) NOT NULL,     -- 'clean', 'infected', 'warning', 'error'
    files_scanned INTEGER,
    infections_found INTEGER DEFAULT 0,
    scan_duration_seconds INTEGER,
    log_file_path TEXT,
    summary JSONB,                   -- Detailed scan metrics
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_malware_scans_date ON blueteam.malware_scans(scan_date DESC);
CREATE INDEX idx_malware_scans_type ON blueteam.malware_scans(scan_type);
CREATE INDEX idx_malware_scans_status ON blueteam.malware_scans(status);

New Table: blueteam.malware_detections

CREATE TABLE blueteam.malware_detections (
    detection_id SERIAL PRIMARY KEY,
    scan_id INTEGER REFERENCES blueteam.malware_scans(scan_id),
    file_path TEXT NOT NULL,
    malware_signature VARCHAR(255),
    severity VARCHAR(20),            -- 'critical', 'high', 'medium', 'low'
    action_taken VARCHAR(50),        -- 'quarantined', 'deleted', 'reported', 'none'
    detected_at TIMESTAMP NOT NULL DEFAULT NOW(),
    resolved_at TIMESTAMP,
    resolution_notes TEXT
);

CREATE INDEX idx_malware_detections_scan ON blueteam.malware_detections(scan_id);
CREATE INDEX idx_malware_detections_unresolved ON blueteam.malware_detections(resolved_at) WHERE resolved_at IS NULL;

Schema Updates: blueteam.posture_scores

-- Add malware_score column
ALTER TABLE blueteam.posture_scores
ADD COLUMN malware_score NUMERIC(5,2);

-- Update posture calculation to include malware
-- New formula:
-- Overall = Compliance(30%) + RedTeam(25%) + Incident(20%) + Monitoring(15%) + Malware(10%)

2. Log Parser Service

Purpose: Parse malware scan logs and populate database

Location: /opt/claude-workspace/shared-resources/scripts/parse-malware-logs.py

Functionality:

#!/usr/bin/env python3
"""
Parse malware scan logs and insert into PostgreSQL database.
Run after each automated scan completes.
"""

import re
import json
from datetime import datetime
from pathlib import Path
import psycopg2

LOG_DIR = Path("/var/log/malware-scans")
DB_CONFIG = {
    "host": "localhost",
    "database": "blueteam",
    "user": "blueteam_app",
    "password": "from_config_or_env"
}

class ClamAVParser:
    """Parse ClamAV scan logs."""

    def parse(self, log_file: Path) -> dict:
        """Extract scan metrics from ClamAV log."""
        with open(log_file) as f:
            content = f.read()

        # Extract summary section
        summary = {}
        if match := re.search(r"Known viruses: (\d+)", content):
            summary['known_viruses'] = int(match.group(1))
        if match := re.search(r"Scanned files: (\d+)", content):
            summary['files_scanned'] = int(match.group(1))
        if match := re.search(r"Infected files: (\d+)", content):
            summary['infected_files'] = int(match.group(1))
        if match := re.search(r"Time: ([\d.]+) sec", content):
            summary['duration_seconds'] = int(float(match.group(1)))

        # Extract infected files
        infections = []
        for match in re.finditer(r"^(.+): (.+) FOUND$", content, re.MULTILINE):
            infections.append({
                'file_path': match.group(1).strip(),
                'signature': match.group(2).strip(),
                'severity': self._assess_severity(match.group(2))
            })

        status = 'infected' if infections else 'clean'

        return {
            'scan_type': 'clamav',
            'status': status,
            'files_scanned': summary.get('files_scanned', 0),
            'infections_found': len(infections),
            'scan_duration_seconds': summary.get('duration_seconds', 0),
            'log_file_path': str(log_file),
            'summary': summary,
            'infections': infections
        }

    def _assess_severity(self, signature: str) -> str:
        """Determine severity based on malware signature."""
        sig_lower = signature.lower()
        if any(x in sig_lower for x in ['backdoor', 'trojan', 'ransomware', 'rootkit']):
            return 'critical'
        elif any(x in sig_lower for x in ['webshell', 'exploit', 'malware']):
            return 'high'
        elif 'suspicious' in sig_lower:
            return 'medium'
        else:
            return 'low'


class MaldetParser:
    """Parse maldet (Linux Malware Detect) logs."""
    # Similar structure to ClamAVParser


class RkhunterParser:
    """Parse rkhunter logs."""
    # Parse warning counts, suspect files


class ChkrootkitParser:
    """Parse chkrootkit logs."""
    # Parse INFECTED markers


def insert_scan_results(db_conn, scan_data: dict):
    """Insert scan results into database."""
    with db_conn.cursor() as cur:
        # Insert main scan record
        cur.execute("""
            INSERT INTO blueteam.malware_scans
            (scan_type, scan_date, status, files_scanned, infections_found,
             scan_duration_seconds, log_file_path, summary)
            VALUES (%(scan_type)s, NOW(), %(status)s, %(files_scanned)s,
                    %(infections_found)s, %(scan_duration_seconds)s,
                    %(log_file_path)s, %(summary)s::jsonb)
            RETURNING scan_id
        """, scan_data)

        scan_id = cur.fetchone()[0]

        # Insert individual detections
        if scan_data.get('infections'):
            for infection in scan_data['infections']:
                cur.execute("""
                    INSERT INTO blueteam.malware_detections
                    (scan_id, file_path, malware_signature, severity, action_taken)
                    VALUES (%s, %s, %s, %s, %s)
                """, (scan_id, infection['file_path'], infection['signature'],
                      infection['severity'], 'reported'))

        db_conn.commit()
        return scan_id


def main():
    """Parse recent logs and update database."""
    conn = psycopg2.connect(**DB_CONFIG)

    # Parse today's ClamAV log
    today = datetime.now().strftime("%Y%m%d")
    clamav_log = LOG_DIR / f"clamav-{today}.log"
    if clamav_log.exists():
        parser = ClamAVParser()
        scan_data = parser.parse(clamav_log)
        insert_scan_results(conn, scan_data)
        print(f"Parsed ClamAV: {scan_data['status']}, "
              f"{scan_data['files_scanned']} files, "
              f"{scan_data['infections_found']} infections")

    # Parse maldet, rkhunter, chkrootkit similarly

    conn.close()


if __name__ == "__main__":
    main()

Integration: Add to automated scan scripts

# In /usr/local/bin/clamav-daily-scan.sh, after scan completes:
/usr/bin/python3 /opt/claude-workspace/shared-resources/scripts/parse-malware-logs.py

3. Dashboard API Endpoint

New File: /var/www/html/alfred/dashboard/security-dashboard/api/malware.php

<?php
header('Content-Type: application/json');

$userId = $_SERVER['HTTP_X_AUTH_USER_ID'] ?? null;
if (!$userId) {
    http_response_code(401);
    echo json_encode(['error' => 'Unauthorized']);
    exit;
}

require_once __DIR__ . '/lib/db.php';

try {
    $pdo = getSecurityDb();

    // Get latest scan results by type
    $stmt = $pdo->query("
        SELECT
            scan_type,
            scan_date,
            status,
            files_scanned,
            infections_found,
            scan_duration_seconds,
            summary
        FROM blueteam.malware_scans
        WHERE scan_id IN (
            SELECT MAX(scan_id)
            FROM blueteam.malware_scans
            GROUP BY scan_type
        )
        ORDER BY scan_type
    ");
    $latest_scans = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Get recent detections (last 30 days, unresolved)
    $stmt = $pdo->query("
        SELECT
            d.detection_id,
            d.file_path,
            d.malware_signature,
            d.severity,
            d.action_taken,
            d.detected_at,
            s.scan_type
        FROM blueteam.malware_detections d
        JOIN blueteam.malware_scans s ON d.scan_id = s.scan_id
        WHERE d.resolved_at IS NULL
          AND d.detected_at > NOW() - INTERVAL '30 days'
        ORDER BY d.detected_at DESC
        LIMIT 50
    ");
    $active_detections = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Get scan history (last 30 days)
    $stmt = $pdo->query("
        SELECT
            scan_type,
            DATE(scan_date) as scan_day,
            COUNT(*) as scan_count,
            SUM(infections_found) as total_infections,
            SUM(files_scanned) as total_files
        FROM blueteam.malware_scans
        WHERE scan_date > NOW() - INTERVAL '30 days'
        GROUP BY scan_type, DATE(scan_date)
        ORDER BY scan_day DESC, scan_type
    ");
    $scan_history = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Calculate malware score
    // Score = 100 - (critical_infections * 30 + high * 20 + medium * 10 + low * 5)
    $stmt = $pdo->query("
        SELECT
            severity,
            COUNT(*) as count
        FROM blueteam.malware_detections
        WHERE resolved_at IS NULL
        GROUP BY severity
    ");
    $severity_counts = ['critical' => 0, 'high' => 0, 'medium' => 0, 'low' => 0];
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $severity_counts[$row['severity']] = (int)$row['count'];
    }

    $malware_score = max(0, 100 - (
        $severity_counts['critical'] * 30 +
        $severity_counts['high'] * 20 +
        $severity_counts['medium'] * 10 +
        $severity_counts['low'] * 5
    ));

    // Days since last scan by type
    $stmt = $pdo->query("
        SELECT
            scan_type,
            EXTRACT(EPOCH FROM (NOW() - MAX(scan_date)))/86400 as days_since
        FROM blueteam.malware_scans
        GROUP BY scan_type
    ");
    $last_scan_days = [];
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $last_scan_days[$row['scan_type']] = round((float)$row['days_since'], 1);
    }

    echo json_encode([
        'malware_score' => $malware_score,
        'latest_scans' => $latest_scans,
        'active_detections' => $active_detections,
        'scan_history' => $scan_history,
        'severity_counts' => $severity_counts,
        'last_scan_days' => $last_scan_days
    ]);

} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Database query failed']);
}

4. Dashboard UI Components

4.1 Update Posture Tab

Add malware score card to /var/www/html/alfred/dashboard/security-dashboard/index.php:

<!-- In posture tab, add 5th score card -->
<div class="score-card score-card-malware">
    <div class="score-card-value" id="score-malware">--</div>
    <div class="score-card-label">Malware</div>
    <div class="score-card-weight">10%</div>
</div>

4.2 Add Malware Tab

New tab in navigation:

<nav class="tab-nav">
    <a href="#posture" class="active">Posture</a>
    <a href="#compliance">Compliance</a>
    <a href="#incidents">Incidents</a>
    <a href="#redteam">Red Team</a>
    <a href="#malware">Malware</a> <!-- NEW -->
</nav>

4.3 Malware Tab Content

<!-- Tab 5: Malware -->
<div id="tab-malware" class="tab-content">

    <!-- Summary Cards -->
    <div class="malware-summary">
        <div class="summary-card">
            <div class="summary-icon">πŸ›‘οΈ</div>
            <div class="summary-value" id="malware-score-display">--</div>
            <div class="summary-label">Malware Defense Score</div>
        </div>

        <div class="summary-card">
            <div class="summary-icon">πŸ”</div>
            <div class="summary-value" id="total-scans">--</div>
            <div class="summary-label">Scans Today</div>
        </div>

        <div class="summary-card summary-card-danger" id="active-threats-card">
            <div class="summary-icon">⚠️</div>
            <div class="summary-value" id="active-threats">--</div>
            <div class="summary-label">Active Threats</div>
        </div>

        <div class="summary-card">
            <div class="summary-icon">πŸ“</div>
            <div class="summary-value" id="files-scanned">--</div>
            <div class="summary-label">Files Scanned (24h)</div>
        </div>
    </div>

    <!-- Latest Scan Results -->
    <div class="section-title">Latest Scan Results</div>
    <div class="scan-results-grid" id="scan-results-grid">
        <!-- Populated by JavaScript -->
    </div>

    <!-- Active Detections -->
    <div class="section-title">
        Active Detections
        <span class="count-badge" id="detections-count">0</span>
    </div>
    <div class="detections-table-container">
        <table class="detections-table" id="detections-table">
            <thead>
                <tr>
                    <th>Severity</th>
                    <th>File Path</th>
                    <th>Malware Signature</th>
                    <th>Detected</th>
                    <th>Scanner</th>
                    <th>Action</th>
                    <th>Options</th>
                </tr>
            </thead>
            <tbody id="detections-tbody">
                <!-- Populated by JavaScript -->
            </tbody>
        </table>
    </div>

    <!-- Scan History Chart -->
    <div class="section-title">30-Day Scan History</div>
    <div class="chart-container">
        <canvas id="malware-history-chart"></canvas>
    </div>

</div>

4.4 JavaScript Integration

// In security dashboard JS
async function loadMalwareData() {
    try {
        const response = await fetch('/security-dashboard/api/malware.php', {
            headers: { 'X-Auth-User-Id': userId }
        });
        const data = await response.json();

        // Update score
        document.getElementById('score-malware').textContent = data.malware_score;
        document.getElementById('malware-score-display').textContent = data.malware_score;

        // Update summary cards
        document.getElementById('active-threats').textContent =
            Object.values(data.severity_counts).reduce((a, b) => a + b, 0);

        // Populate scan results grid
        renderScanResults(data.latest_scans);

        // Populate detections table
        renderDetections(data.active_detections);

        // Render history chart
        renderMalwareHistoryChart(data.scan_history);

    } catch (error) {
        console.error('Failed to load malware data:', error);
    }
}

function renderScanResults(scans) {
    const grid = document.getElementById('scan-results-grid');
    grid.innerHTML = scans.map(scan => `
        <div class="scan-result-card scan-${scan.status}">
            <div class="scan-header">
                <span class="scan-type">${scan.scan_type.toUpperCase()}</span>
                <span class="scan-status status-${scan.status}">${scan.status}</span>
            </div>
            <div class="scan-metrics">
                <div class="metric">
                    <span class="metric-value">${scan.files_scanned.toLocaleString()}</span>
                    <span class="metric-label">Files Scanned</span>
                </div>
                <div class="metric">
                    <span class="metric-value ${scan.infections_found > 0 ? 'danger' : ''}">${scan.infections_found}</span>
                    <span class="metric-label">Infections</span>
                </div>
            </div>
            <div class="scan-footer">
                <span class="scan-time">${formatTimestamp(scan.scan_date)}</span>
                <a href="#" onclick="viewScanLog('${scan.scan_type}')" class="scan-log-link">View Log</a>
            </div>
        </div>
    `).join('');
}

function renderDetections(detections) {
    const tbody = document.getElementById('detections-tbody');
    document.getElementById('detections-count').textContent = detections.length;

    if (detections.length === 0) {
        tbody.innerHTML = '<tr><td colspan="7" class="no-data">No active detections</td></tr>';
        return;
    }

    tbody.innerHTML = detections.map(d => `
        <tr class="detection-row severity-${d.severity}">
            <td><span class="severity-badge severity-${d.severity}">${d.severity}</span></td>
            <td class="file-path"><code>${d.file_path}</code></td>
            <td class="signature">${d.malware_signature}</td>
            <td>${formatTimestamp(d.detected_at)}</td>
            <td>${d.scan_type}</td>
            <td>${d.action_taken}</td>
            <td>
                <button onclick="resolveDetection(${d.detection_id})" class="btn-resolve">Resolve</button>
                <button onclick="viewDetails(${d.detection_id})" class="btn-details">Details</button>
            </td>
        </tr>
    `).join('');
}

5. Alert Integration

Update: /var/www/html/alfred/dashboard/security-dashboard/api/alerts.php

Add malware detections to alerts:

// Get recent malware detections
$stmt = $pdo->query("
    SELECT
        'malware' as type,
        'critical' as severity,
        d.detected_at as timestamp,
        CONCAT('Malware detected: ', d.malware_signature, ' in ',
               SUBSTRING(d.file_path FROM '[^/]+$')) as message,
        JSON_BUILD_OBJECT(
            'detection_id', d.detection_id,
            'file_path', d.file_path,
            'scanner', s.scan_type
        ) as metadata
    FROM blueteam.malware_detections d
    JOIN blueteam.malware_scans s ON d.scan_id = s.scan_id
    WHERE d.resolved_at IS NULL
      AND d.severity IN ('critical', 'high')
      AND d.detected_at > NOW() - INTERVAL '7 days'
    ORDER BY d.detected_at DESC
    LIMIT 10
");
$malware_alerts = $stmt->fetchAll(PDO::FETCH_ASSOC);

6. Updated Posture Score Calculation

Update: /var/www/html/alfred/dashboard/security-dashboard/api/posture.php

// Add malware score calculation
$stmt = $pdo->query("
    SELECT
        severity,
        COUNT(*) as count
    FROM blueteam.malware_detections
    WHERE resolved_at IS NULL
    GROUP BY severity
");
$severity_counts = ['critical' => 0, 'high' => 0, 'medium' => 0, 'low' => 0];
while ($row = $stmt->fetch()) {
    $severity_counts[$row['severity']] = (int)$row['count'];
}

$malwareScore = max(0, 100 - (
    $severity_counts['critical'] * 30 +
    $severity_counts['high'] * 20 +
    $severity_counts['medium'] * 10 +
    $severity_counts['low'] * 5
));

// Update overall score calculation
$overallScore = round(
    $complianceScore * 0.30 +   // Reduced from 35%
    $redteamScore * 0.25 +      // Reduced from 30%
    $incidentScore * 0.20 +
    $monitoringScore * 0.15 +
    $malwareScore * 0.10,       // NEW
    2
);

$current['malware'] = (float) $malwareScore;

Implementation Phases

Phase 1: Database Setup (1-2 hours)

Tasks: 1. Create database schema (tables, indexes) 2. Add sample data for testing 3. Verify database permissions

Deliverables: - SQL migration script - Database schema documentation - Test data fixtures

Phase 2: Log Parser Development (3-4 hours)

Tasks: 1. Write Python log parser for each scanner type 2. Add database insertion logic 3. Create unit tests 4. Integrate with existing scan scripts

Deliverables: - parse-malware-logs.py script - Parser unit tests - Updated scan scripts (clamav-daily-scan.sh, etc.)

Phase 3: API Endpoint (2-3 hours)

Tasks: 1. Create /api/malware.php endpoint 2. Implement query logic for latest scans, detections, history 3. Add malware score calculation 4. Test API responses

Deliverables: - api/malware.php - API documentation - Test cases

Phase 4: UI Components (4-5 hours)

Tasks: 1. Add malware score card to posture tab 2. Create new "Malware" tab 3. Build scan results grid 4. Build detections table 5. Add history chart 6. Style all components

Deliverables: - Updated index.php - CSS updates for malware components - JavaScript modules for data loading/rendering

Phase 5: Integration & Testing (2-3 hours)

Tasks: 1. Update posture score calculation 2. Add malware alerts to alerts system 3. End-to-end testing 4. Fix bugs and polish UI

Deliverables: - Updated posture.php - Updated alerts.php - Test results documentation

Phase 6: Deployment (1 hour)

Tasks: 1. Deploy database schema to production 2. Deploy code to Alfred dashboard 3. Run initial log parsing 4. Verify dashboard displays correctly 5. Monitor for issues

Deliverables: - Deployment checklist - Production verification - Monitoring alerts


Total Effort Estimate

Development: 12-17 hours Testing: 2-3 hours Deployment: 1 hour Total: 15-21 hours (2-3 days)


Technical Requirements

Server Requirements

Dependencies


Risk Assessment

High Risk

  1. Log parsing failures - Scan log formats may vary
  2. Mitigation: Robust regex patterns, error handling

  3. Database performance - Large scan history could slow queries

  4. Mitigation: Proper indexing, data retention policies

Medium Risk

  1. UI complexity - Many data points to display
  2. Mitigation: Progressive enhancement, lazy loading

  3. Alert fatigue - Too many malware alerts

  4. Mitigation: Severity filtering, deduplication

Low Risk

  1. Browser compatibility - Modern JS/CSS features
  2. Mitigation: Use stable, well-supported features

Success Metrics

  1. Visibility: All scan results visible in dashboard within 5 minutes of completion
  2. Accuracy: Malware score correctly reflects active threats
  3. Performance: Dashboard loads in <2 seconds
  4. Usability: Users can drill down to individual detections in <3 clicks
  5. Reliability: Log parsing succeeds >99% of the time

Future Enhancements

Phase 7 (Future)

  1. Real-time scan progress monitoring
  2. Manual scan triggers from dashboard
  3. Automated remediation workflows
  4. Integration with incident response system
  5. Compliance mapping (map scans to controls)
  6. Advanced analytics (trend analysis, ML anomaly detection)
  7. Mobile notifications for critical detections
  8. Integration with SIEM systems

Appendices

A. Database ERD

malware_scans
β”œβ”€β”€ scan_id (PK)
β”œβ”€β”€ scan_type
β”œβ”€β”€ scan_date
β”œβ”€β”€ status
β”œβ”€β”€ files_scanned
β”œβ”€β”€ infections_found
β”œβ”€β”€ scan_duration_seconds
β”œβ”€β”€ log_file_path
β”œβ”€β”€ summary (JSONB)
└── created_at

malware_detections
β”œβ”€β”€ detection_id (PK)
β”œβ”€β”€ scan_id (FK -> malware_scans)
β”œβ”€β”€ file_path
β”œβ”€β”€ malware_signature
β”œβ”€β”€ severity
β”œβ”€β”€ action_taken
β”œβ”€β”€ detected_at
β”œβ”€β”€ resolved_at
└── resolution_notes

posture_scores
β”œβ”€β”€ ... (existing columns)
└── malware_score (NEW)

B. API Response Examples

GET /api/malware.php

{
  "malware_score": 95.0,
  "latest_scans": [
    {
      "scan_type": "clamav",
      "scan_date": "2026-03-06 02:00:00",
      "status": "clean",
      "files_scanned": 156789,
      "infections_found": 0,
      "scan_duration_seconds": 1245
    }
  ],
  "active_detections": [
    {
      "detection_id": 42,
      "file_path": "/var/www/html/example.com/shell.php",
      "malware_signature": "Php.Webshell.Generic",
      "severity": "critical",
      "action_taken": "quarantined",
      "detected_at": "2026-03-05 15:30:00",
      "scan_type": "clamav"
    }
  ],
  "severity_counts": {
    "critical": 1,
    "high": 0,
    "medium": 0,
    "low": 0
  },
  "last_scan_days": {
    "clamav": 0.1,
    "maldet": 0.2,
    "rkhunter": 2.5
  }
}

C. UI Mockups

See attached wireframes for: - Malware tab layout - Scan results cards - Detections table - History chart


Plan Status: READY FOR REVIEW Next Steps: Review plan, obtain approval, begin Phase 1